﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

using LibMessage.Filters;
using LibMessage.Models;

using Newtonsoft.Json;

using SuperSocket.ClientEngine;
using SuperSocket.ClientEngine.Protocol;
using SuperSocket.ProtoBase;

namespace LibMessage
{
    internal static class TcpClient
    {
        private static EasyClient<StringPackageInfo> _client;
        private static IPEndPoint _ep;
        private static Action<MessagePack> _connectedCallback;
        private static Action<MessagePack> _disconnectedCallback;
        private static Action<MessagePack> _dataReceivedCallback;
        private static Queue<MessagePack> _pendingToSend = new Queue<MessagePack>();

        static TcpClient()
        {
            Task.Run(SendData);
        }

        public static void InitCallback(Action<MessagePack> connected, Action<MessagePack> disconnected, Action<MessagePack> dataReceived)
        {
            _connectedCallback = connected;
            _disconnectedCallback = disconnected;
            _dataReceivedCallback = dataReceived;
        }

        private static void SendData()
        {
            while (true)
            {
                try
                {
                    if (_client != null)
                    {
                        while (_pendingToSend.Count > 0 && _client.IsConnected)
                        {
                            var pack = _pendingToSend.Dequeue();
                            var mp = new MessageModel
                            {
                                Data = pack.Data
                            };
                            string tmpStr = JsonConvert.SerializeObject(mp);
                            byte[] tmpData = Encoding.UTF8.GetBytes(tmpStr);
                            string jStr = pack.Cmd + " " + Convert.ToBase64String(tmpData) + Environment.NewLine;
                            byte[] data = Encoding.UTF8.GetBytes(jStr);
                            _client.Send(new ArraySegment<byte>(data, 0, data.Length));
                        }
                    }
                }
                catch (Exception)
                {
                }
                System.Threading.Thread.Sleep(100);
            }
        }

        public static async void ConnectToServer(string server, int port)
        {
            try
            {
                if (_client != null)
                    await _client.Close();
                _client = new EasyClient<StringPackageInfo>() { ReceiveBufferSize = 81920 };
                _client.Initialize(new MsgRecvFilter());
                _client.Connected += Client_Connected; // 连接成功
                _client.NewPackageReceived += Client_NewPackageReceived; // 收到数据
                _client.Closed += Client_Closed; // 会话关闭
                _client.Error += Client_Error; // 会话异常
                                               // 连接服务器
                _ep = new IPEndPoint(IPAddress.Parse(server), port);
                var result = await _client.ConnectAsync(_ep);
                if (!result)
                    throw new Exception("连接失败");
            }
            catch (Exception ex)
            {
                MessagePack mp = new MessagePack { Result = false, ErrCode = "0001", ErrString = ex.Message, SendDate = DateTime.Now };
                _connectedCallback?.Invoke(mp);
            }
        }

        private static void Client_Error(object sender, ErrorEventArgs e)
        {
        }

        private static void Client_Closed(object sender, EventArgs e)
        {
            MessagePack mp = new MessagePack { ErrString = "连接已断开" };
            _disconnectedCallback(mp);
        }

        private static void Client_NewPackageReceived(object sender, PackageEventArgs<StringPackageInfo> e)
        {
            switch (e.Package.Key)
            {
                case "heartbeat":
                    // 心跳包
                    break;

                case "loginresult":
                    // 登录结果
                    string jStr = e.Package.Body;
                    var pack = JsonConvert.DeserializeObject<MessageModel>(Encoding.UTF8.GetString(Convert.FromBase64String(jStr)));
                    MessagePack retmp = new MessagePack { Cmd = e.Package.Key, Data = pack.Data, SendDate = pack.SendDate, Result = true };
                    _dataReceivedCallback?.Invoke(retmp);
                    break;

                case "wcast":
                    // 广播数据
                    jStr = e.Package.Body;
                    pack = JsonConvert.DeserializeObject<MessageModel>(Encoding.UTF8.GetString(Convert.FromBase64String(jStr)));
                    retmp = new MessagePack { Cmd = e.Package.Key, Data = pack.Data, SendDate = pack.SendDate, Result = true };
                    _dataReceivedCallback?.Invoke(retmp);
                    break;

                default:
                    break;
            }
        }

        private static void Client_Connected(object sender, EventArgs e)
        {
            MessagePack mp = new MessagePack { Result = true };
            _connectedCallback?.Invoke(mp);
        }

        public static void SendData(MessagePack pack)
        {
            _pendingToSend.Enqueue(pack);
        }
    }
}